import { CancelTokenItem, HttpAxiosConfig, HttpResponseData } from '@/domain/axios';
import axios, { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';

const CODE_MESSAGE = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队（异步任务）。',
    204: '删除数据成功。',
    400: '信息校验失败',
    401: '用户没有权限（令牌、用户名、密码错误）。',
    403: '用户得到授权，但是访问被禁止',
    404: '请求的资源不存在',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除，且不会再得到的。',
    422: '当创建一个对象时，发生一个验证错误。',
    500: '服务器发生错误，请检查服务器。',
    502: '网关错误。',
    503: '服务不可用，服务器暂时过载或维护。',
    504: '网关超时。',
};

class HttpAxios {
    /**
     * axios实例
     */
    instance: AxiosInstance;

    /**
     * 请求方式
     */
    method: string = 'post';
    /**
     * 超时时间
     */
    timeout: number = 300 * 1000;
    /**
     * loading实例
     */
    loadingInstance: any = null;
    /**
     * 当前正在进行的请求计数器，用于控制loading的显示和隐藏
     */
    requestCount: number = 0;
    /**
     * axios cancelToken 数组，方便处理取消请求
     */
    cancelTokenArr: CancelTokenItem[] = [];
    /**
     * 是否在切换页面时清除请求
     */
    clearFlag = true;

    constructor(config: HttpAxiosConfig = {}) {
        let instance = axios.create(config);
        // 请求拦截
        instance.interceptors.request.use(
            (config: InternalAxiosRequestConfig) => this.onBeforeRequest(config),
            (config: InternalAxiosRequestConfig) => this.onRequestRejected(config)
        );
        // 响应拦截
        instance.interceptors.response.use(
            (response: any) => this.onResponseResolved(response),
            (error: any) => this.onResponseRejected(error)
        );

        this.instance = instance;
    }

    /**
     * 显示loading
     * @param config 请求配置
     */
    private showLoading(config: HttpAxiosConfig) {
        if (config.showLoading && !this.loadingInstance) {
        }
        this.requestCount++;
    }
    /**
     * 隐藏loading
     */
    private hideLoading() {
        this.requestCount--;
        if (this.requestCount === 0 && this.loadingInstance) {
            // this.loadingInstance.close();
            this.loadingInstance = null;
        }
    }

    /**
     * 请求拦截，处理header部分传值及是否显示loading
     * @param config
     */
    onBeforeRequest = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
        // 显示loading
        this.showLoading(config);
        let _config = {
            withCredentials: false, // 处理跨域问题
            timeout: this.timeout,
        };
        if (this.method == 'formate') {
            config.headers.setContentType('application/x-www-form-urlencoded; charset=UTF-8');
        }
        config.cancelToken = new axios.CancelToken((cancel) => {
            // 添加url，方便取消特定url的请求
            this.cancelTokenArr.push({ cancel, url: config.url || '' });
        });
        return Object.assign({}, config, _config);
    };

    /**
     * 请求拦截失败
     * @param error
     */
    onRequestRejected = (error: any): Promise<any> => {
        return Promise.reject(error);
    };

    /**
     * 返回拦截
     * @param response
     * @returns
     */
    onResponseResolved = async (response: AxiosResponse) => {
        let responseData: HttpResponseData<any> = response.data || {};
        responseData.
        // 判断返回成功码
        const { code, data } = responseData;
        if (code === 200) {
            return data || '';
        }
        await this.retryRequest(response.config);
        this.hideLoading();
        // TODO 弹出错误信息
        // const { code, message } = responseData;
        return undefined;
    };

    /**
     * 重试请求
     * @param config
     */
    retryRequest = async (config) => {
        // 添加重试逻辑
        // 只有在有重试次数时，才回进行重试
        if (config && config.maxRetries && config.retryCount < config.maxRetries) {
            config.retryCount = (config.retryCount || 0) + 1;
            await sleep(config.retryDelay || 1000); // 重试前等待1秒
            return this.instance(config);
        }
    };

    /**
     * 处理请求错误时情况 根据不同的状态码
     * @param error
     */
    onResponseRejected = async (error) => {
        await this.retryRequest(error.config);
        // 隐藏loading
        if (this.loadingInstance) {
            this.loadingInstance.close();
            this.loadingInstance = null;
        }
        this.hideLoading();
        if (/timeout/g.test(error)) {
            return Promise.reject(error);
        }
        // console.log(error);
        const { response } = error;
        const { status, statusText, data } = response || {};
        let msg = data?.message || CODE_MESSAGE[status] || statusText;
        return Promise.reject(error);
    };

    /**
     * 发送请求
     * @param url
     * @param params
     * @param method
     * @param config
     * @returns
     */
    sendRequest = (url, params, method = 'post', config = {}) => {
        if (!this.instance) {
            return;
        }
        this.method = method;
        if (config?.maxRetries) {
            config.retryCount = 0;
        }
        const _method = method.toLocaleLowerCase();
        if (_method === 'get') {
            params = {
                params: params,
            };
            return this.instance.get(url, { ...params, ...config });
        }
        if (_method === 'formdata') {
            let reqData = new FormData();
            for (let key in params) {
                reqData.append(key, params[key]);
            }
            return this.instance.post(url, reqData, config);
        }
        return this.instance.post(url, params, config);
    };

    /**
     * 取消请求
     * @param url
     */
    async cancelRequest(url) {
        if (this.cancelTokenArr.length === 0) {
            return;
        }
        for (let i = 0; i < this.cancelTokenArr.length; i++) {
            if (this.cancelTokenArr[i].url === url) {
                this.cancelTokenArr[i].cancel();
                this.cancelTokenArr.splice(i, 1);
                break;
            }
        }
    }
    /**
     * 清除axios 请求
     */
    async clearRequests() {
        if (this.cancelTokenArr.length === 0 || !this.clearFlag) {
            return;
        }
        this.cancelTokenArr.forEach((ele) => {
            ele.cancel();
        });
        this.cancelTokenArr = [];
    }
}
export const getHttpAxiosInstance = () => {
    let instance = null;
    return () => {
        if (!instance) {
            instance = new HttpAxios({});
        }
        return instance;
    };
};
